#coding:utf-8
"""
USER_DATA:
    client->server: 手机号+测试用例数据
    server->client: 手机号+log数据
USER_PATHNAME:
    client->server: 手机号+路径名
    server->client: True/False
USER_MSG:
    client->server: forbidden
    server->client: strings.
USER_START:
    client->server: 手机号, 请求测试开始
    server->client: 手机号, 测试已开始
USER_STOP:
    client->server: 手机号, 请求该测试停止
    server->client: 手机号, 测试已停止
USER_VEHICLESTATE:
    client->server: 手机号, 询问该车在线状态
    server->client: 手机号+状态码:False,未连接;True,已连接;
USER_PROCESS:
    client->server: 手机号, 请求测试进度
    server->client: 手机号+进度数据: (process, total)
USER_VEHICLETESTSTATE:
    client->server: 手机号, 询问该车测试状态
    server->client: 手机号+状态码:0,不在测试中;1,测试中;2,测试暂停;
"""

import pickle
from twisted.internet import reactor, defer
from twisted.internet.protocol import Protocol, Factory, ClientFactory
import time
import base
import stock
import report



USER_DATA = "~data~"
USER_PATHNAME = "~pathname~"
USER_MSG = "~message~"
USER_START = "~start~"
USER_STOP = "~stop~"
USER_PAUSE = "~pause~"
USER_VEHICLESTATE = "~vehiclestate~"
USER_VEHICLETESTSTATE = "~teststate~"
USER_PROCESS = "~process~"


HEADING = "<--"
ENDING = "-->"

# -----------------------------------
def userPack(type, data):
    s = pickle.dumps((type, data))
    return "%s%s%s"%(HEADING, s, ENDING)

def userUnpack(data):
    dataList = []
    start = 0
    while True:
        head = data.find(HEADING, start)
        end = data.find(ENDING, head)
        if head >= start and end > head:
            s = data[head:end].lstrip(HEADING)
            dataList.append(pickle.loads(s))
            start = end + len(ENDING)
        else:
            break
    return dataList

# -------------------------------------

class UserServerProtocol(Protocol):
    def __init__(self, player):
        self.player = player
        self.router = {USER_START: self.player.startTest,
                       USER_STOP: self.player.stopTest,
                       USER_DATA: self.player.setVehicleData,
                       USER_PATHNAME: self.player.setReportPath,
                       USER_VEHICLESTATE: self.player.requestVehicleState,
                       USER_PROCESS: self.player.requestTestProcess,
                       }
        
    def connectionMade(self):
        self.factory.plist.append(self)
        #self.player.requestAllVehicleState()
        
    def connectionLost(self, reason):
        self.factory.plist.remove(self)

    def dataReceived(self, data):
        for type, data in userUnpack(data):
            #report.info("read user:[%s]%s"%(type, data))
            callable = self.router.get(type, None)
            if callable: callable(*data)
      
    def write(self, msgType, *msgData):
        #report.info("write user:[%s]%s"%(msgType, msgData))
        s = userPack(msgType, msgData)
        self.transport.write(s) 
    
    
class UserServer(Factory):
    def __init__(self, player):
        self.player = player
        player.user = self
        self.plist = []
        
    def buildProtocol(self, address):
        p = UserServerProtocol(self.player)
        p.factory = self
        return p
    
    def write(self, *args, **kw):
        for p in self.plist:
            p.write(*args, **kw)
    
    def sendMessage(self, msg):
        self.write(USER_MSG, msg)
    
    def sendData(self, tel, data):
        self.write(USER_DATA, tel, data)
            
    def sendVehicleState(self, tel, state):
        self.write(USER_VEHICLESTATE, tel, state)
            
    def sendTestProcess(self, tel, testTime, totalTime):
        self.write(USER_PROCESS, tel, testTime, totalTime)
            
    def sendTestStart(self, tel):
        self.write(USER_VEHICLETESTSTATE, tel, 1)
            
    def sendTestStop(self, tel):
        self.write(USER_VEHICLETESTSTATE, tel, 0)
            
    def sendTestPause(self, tel):
        self.write(USER_VEHICLETESTSTATE, tel, 2)

# ------------------------------------------

class UserClientProtocol(Protocol):
    def __init__(self, ctrl):
        self.state = ctrl.state_panel
        self.vehicle = ctrl.vehicle_panel
        self.status = ctrl.status_panel
        self.router = {USER_MSG: self.state.writeMessage,
                       USER_DATA: self.state.writeData,
                       USER_PATHNAME: ctrl,
                       USER_VEHICLESTATE: self.vehicle.updateVehicleState,
                       USER_VEHICLETESTSTATE:self.updateTestState,
                       USER_PROCESS: self.state.writeState,
                       }
    
    def updateTestState(self, tel, state):
        if state == 0:
            self.state.showVehicleTestStoped(tel)
            self.vehicle.updateVehicleTestState(tel, False)
        elif state == 1:
            self.state.showVehicleTestStarted(tel)
            self.vehicle.updateVehicleTestState(tel, True)
        elif state == 2:
            self.vehicle.updateVehicleState
    
        
    def routeControl(self, type, *data):
        callable = self.router.get(type, None)
        if callable: callable(*data)
        
    def connectionMade(self):
        if self.state:
            self.state.writeMessage(u"服务器已连接")
            self.status.showConnection(True)
    
    def dataReceived(self, data):
        for msgType, msgData in userUnpack(data):
            self.routeControl(msgType, *msgData)
            
    def connectionLost(self, reason):
        if self.state:
            self.state.writeMessage(u"服务器连接丢失")
            self.status.showConnection(False)
    
    def loseConnection(self):
        self.transport.loseConnection()
    
    def write(self, msgType, *msgData):
        # python2.5 存在一个问题, socket只能发送string类型不能发送unicode类型.
        # twisted底部tcp可能重写过,不会像python2.5中的socket遇到非string类型时,
        # 将其转换成string再发送.twisted的socket似乎默认为不处理非string类型的数据.
        msg = userPack(msgType, msgData)
        assert isinstance(msg, str), "Msg is only sended as string type. \
            msg:%s type(%s)"%(msg, type(msg))
        self.transport.write(msg)
        
class InfoControlMixin:
    def writeData(self, *args, **kw):
        if self.onlyProtocol:
            self.onlyProtocol.write(*args, **kw)
            
    def requestStartTest(self, *args, **kw):
        self.writeData(USER_START, *args, **kw)
            
    def requestStopTest(self, *args, **kw):
        self.writeData(USER_STOP, *args, **kw)
        
    def setData(self, *args, **kw):
        self.writeData(USER_DATA, *args, **kw)
            
    def setPathName(self, *args, **kw):
        self.writeData(USER_PATHNAME, *args, **kw)
            
    def requestVehicleState(self, *args, **kw):
        self.writeData(USER_VEHICLESTATE, *args, **kw)
            
    def requestTestProcess(self, *args, **kw):
        self.writeData(USER_PROCESS, *args, **kw)

class UserConnector(ClientFactory, InfoControlMixin):
    delay = 10.0
    onlyProtocol = None
    def __init__(self, control):
        self.ctrl = control
        
    def buildProtocol(self, address):
        self.onlyProtocol = UserClientProtocol(self.ctrl)
        return self.onlyProtocol
        
    def updateConnect(self, host, port):
        self.stopConnect()
        self.connect(host, port)
        
    def stopConnect(self):
        if self.onlyProtocol:
            self.onlyProtocol.loseConnection()
        
    def connect(self, host, port):
        self.host, self.port = host, port
        reactor.connectTCP(host, port, self)
    
    def startedConnecting(self, connector):
        if self.host != connector.host or self.port != connector.port:
            connector.stopConnecting()
        
    def clientConnectionLost(self, connector, reason):
        self.onlyProtocol = None
        self.retry(connector)
        
    def clientConnectionFailed(self, connector, reason):
        self.onlyProtocol = None
        self.retry(connector)

    def retry(self, connector):
        if self.host != connector.host or self.port != connector.port:
            return
        reactor.callLater(self.delay, connector.connect)
        
    def isConnected(self):
        if self.onlyProtocol:
            return True
        else:
            return False
